// Program to implement a one pass assembler for the SIC machine
// Input :asembler language program
// Output:object file, list file

#include <fstream.h>
#include <string.h>
#include <process.h>
#include <strstream.h>
#include <ctype.h>
#include <iomanip.h>
#include <time.h>

class textfile
{
 protected:
	fstream f;
	char line[80];
 public:
	textfile() { }
	textfile(char *fname, char mode);
	~textfile() { f.close(); }
	void readline() { f.getline(line,80); }
	void writeline() { f<<line<<endl; }
	char *getline() { return line; }
	void setline(char *src) { strcpy(line,src); }
	void writeline(streampos pos);
};

textfile::textfile(char *fname, char mode)
{
 if (mode=='r')
	 f.open(fname,ios::in);
 else
	 f.open(fname,ios::out);
 if (!f)
	 {
	  cerr<<"Error opening file "<<fname;
	  exit(-1);
	 }
}

void textfile::writeline(streampos pos)
{
 f.seekp(pos,ios::beg);
 f<<line;
}

class sourcefile:public textfile
{
 public:
  sourcefile(char *fname):textfile(fname,'r') { }
  ~sourcefile() { }
};

class listfile:public textfile
{
 private:
	struct error_node
	{
	 char loc[5];
	 char message[50];
	 error_node *link;
	} *start,*prev;
 public:
	listfile(char *fname,char *src);
	~listfile();
	void writelisting(char *loc,char *_line,char *obj);
	void errormessage(char *loc,char *msg);
	void writeerrors();
};

listfile::listfile(char *fname,char *src):textfile(fname,'w')
{
 start=new error_node;
 start->link=NULL;
 prev=start;
 cout<<endl<<"SIC assembler"
     <<endl<<"Assembling file: "<<src<<endl<<endl;
 ostrstream s(line,80);
 s<<"SIC assembler   "<<endl<<"Source file: "
  <<src<<endl<<ends;
 writeline();
}

listfile::~listfile()
{
 error_node *ptr=start,*next;
 while(ptr!=NULL)
  {
	next=ptr->link;
	delete ptr;
	ptr=next;
  }
}

void listfile::writelisting(char *loc,char *_line,char *obj)
{
 ostrstream s(line,80);
 s<<loc<<"   "<<setiosflags(ios::left)<<setw(30)
  <<_line<<resetiosflags(ios::left)<<"   "<<setw(10)<<obj<<ends;
 writeline();
}

void listfile::errormessage(char *loc,char *msg)
{
 error_node *ptr=new error_node;
 prev->link=ptr;
 prev=ptr;
 strcpy(ptr->loc,loc);
 strcpy(ptr->message,msg);
 ptr->link=NULL;
}

void listfile::writeerrors()
{
 error_node *ptr=start->link;
 ostrstream s(line,80);
 if (ptr!=NULL)
		s<<endl<<"Errors:"<<endl<<"******"<<endl<<ends;
 else
		s<<endl<<"************"<<endl
		 <<"*          *"<<endl
		 <<"* Errors=0 *"<<endl
		 <<"*          *"<<endl
		 <<"************"<<ends;
 writeline();
 cout<<line;
 while(ptr!=NULL)
 {
  ostrstream s(line,80);
  s<<ptr->loc<<":  "<<ptr->message<<ends;
  writeline();
  cout<<line<<endl;
  ptr=ptr->link;
 }
}

class objectfile:public textfile
{
 private:

 struct headerrecord
 {
	char name[7];
	char start_address[7];
	char length[7];
 } header;

 struct textrecord
 {
  char start_address[7];
  char length[3];
  #ifdef RELOCATION_OPTION
			char relocation_bit_mask[13];
  #endif
  char objcode[61];
  int cursize;
 } text;

 struct endrecord
 {
  char entry_point[7];
 } end;

 streampos pos;
 int textrecord_written;

 public:
  objectfile(char *fname):textfile(fname,'w') { pos=13;}
  ~objectfile() { }
  void write_header(char *name,char *start);
  void write_proglength(char *length);
  void write_endrecord(char *entry);
  void add_to_textrecord(char *instrn,char *loc,char ch);
  void initialise_textrecord(char *loc);
  void write_textrecord();
  int room_in_textrecord(char *instrn);
  void add_to_headerrecord(char *name,char *start);
  void adjustname(char *name,int size);
  void adjustnumber(char *name,int size);
};

void objectfile::write_header(char *name,char *start)
{
 add_to_headerrecord(name,start);
 ostrstream s(line,80);
 s<<"H"<<header.name<<header.start_address<<header.length<<ends;
 writeline();
}

void objectfile::add_to_headerrecord(char *name,char *start)
{
 strcpy(header.name,name);
 if (strlen(header.name)!=6) adjustname(header.name,6);
 strcpy(header.start_address,start);
 if (strlen(header.start_address)!=6) adjustnumber(header.start_address,6);
 strcpy(header.length,"000000");
}

void objectfile::adjustname(char *name,int size)
{
 name[size]='\0';
 for(int i=strlen(name);i<size;i++)
	  name[i]=' ';
}

void objectfile::adjustnumber(char *num,int size)
{
 num[size]='\0';
 int k=size-1;
 for(int i=strlen(num)-1;i>=0;i--)
	  num[k--]=num[i];
 while(k>=0)
  num[k--]='0';
}

void objectfile::write_proglength(char *length)
{
 setline(length);
 writeline(pos);
}

void objectfile::write_endrecord(char *entry)
{
 strcpy(end.entry_point,entry);
 if (strlen(end.entry_point)!=6) adjustnumber(end.entry_point,6);
 ostrstream s(line,80);
 s<<"E"<<end.entry_point<<ends;
 writeline();
}

void objectfile::add_to_textrecord(char *instrn,char *loc,char ch='c')
{
 if (!(room_in_textrecord(instrn)))
	{
	 write_textrecord();
	 initialise_textrecord(loc);
	}
 #ifdef RELOCATION_OPTION
		if (ch=='i')
			{
			 if ((text.cursize%6)!=0)
				 {
				  write_textrecord();
				  initialise_textrecord(loc);
				 }
			 text.relocation_bit_mask[text.cursize/6]='1';
			}
 #endif
 strcat(text.objcode,instrn);
 text.cursize=strlen(text.objcode);
}

void objectfile::initialise_textrecord(char *loc)
{
 #ifdef RELOCATION_OPTION
		  for (int i=0;i<12;i++)
			 text.relocation_bit_mask[i]='0';
		  text.relocation_bit_mask[i]='\0';
 #endif
 textrecord_written=0;
 strcpy(text.start_address,loc);
 if (strlen(text.start_address)!=6) adjustnumber(text.start_address,6);
 text.objcode[0]='\0';
 text.cursize=0;
}

void objectfile::write_textrecord()
{
 if (textrecord_written==1) return;
 if(strlen(text.objcode)==0) return;
 textrecord_written=1;
 ostrstream s(text.length,3);
 s<<hex<<setiosflags(ios::uppercase)<<text.cursize/2<<ends;
 if (strlen(text.length)!=2) adjustnumber(text.length,2);
 ostrstream str(line,80);
 #ifdef RELOCATION_OPTION
	  int d=0;
	  int factor=1;
	  for(int i=11;i>=0;i--,factor*=2)
		 d+=(((int)text.relocation_bit_mask[i])-48)*factor;
	  if (!d)
	  str<<"T"<<text.start_address<<text.length<<"000"<<text.objcode<<ends;
	  else
	  str<<"T"<<text.start_address<<text.length<<hex
		  <<setiosflags(ios::uppercase)<<d<<text.objcode<<ends;
 #endif
 str<<"T"<<text.start_address<<text.length<<text.objcode<<ends;
 writeline();
}

int objectfile::room_in_textrecord(char *instrn)
{
 if ((text.cursize+strlen(instrn))>60)
		return 0;
 else
		return 1;
}

const unsigned short int num_opcodes=18;

class opcodetable:public textfile
{
 private:
  struct optabrec
	{
	 char opcode[5];
	 char _value[3];
	} table[num_opcodes];
	int index;
 public:
  opcodetable();
  int isopcode(char *opcode) { return bsearch(opcode,0,num_opcodes-1); }
  int bsearch(char *opcode,int beg,int end);
  char *value() { return table[index]._value; }
};

opcodetable::opcodetable():textfile("opcode.dat",'r')
{
 int i=0;
 while(f)
  {
	readline();
	if (f)
	{
	strcpy(table[i].opcode,line);
	readline();
	strcpy(table[i++]._value,line);
	}
  }
}

int opcodetable::bsearch(char *opcode,int beg,int end)
{
 while(beg<=end)
 {
	int mid=(beg+end)/2;
	int cmp=strcmp(table[mid].opcode,opcode);
	if (cmp==0)
		 {
		  index=mid;
		  return 1;
		 }
	else
	 if (cmp<1)
		  beg=mid+1;
	 else
		  end=mid-1;
 }
 return 0;
}

class codegenerator;
class pass_1;
class scanner
{
 protected:
  char line[80],label[10],opcode[10],operand[10];
  int commentflag;
  int index,indexmode;
 public:
  friend codegenerator;
  friend pass_1;
  void setline(char *_line) { strcpy(line,_line); }
  void scan();
  int comment() { return commentflag; }
  int gettoken(char *symbol);
};

void scanner::scan()
{
 label[0]=opcode[0]=operand[0]='\0';
 index=0;
 indexmode=0;
 if (line[0]=='.')
			commentflag=1;
 else
  {
	 commentflag=0;
			  if (strlen(line)>0)
				 if (gettoken(label))
					 if (gettoken(opcode))
							 gettoken(operand);
  }
}

int scanner::gettoken(char *symbol)
{
 int j=-1;
 while(index<strlen(line))
  {
	if ((line[index]==' ')||(line[index]=='\t')||(line[index]==','))
		break;
	if (j<8)
		 symbol[++j]=line[index];
	index++;
  }
 symbol[++j]='\0';
 if ((index<strlen(line))&&(line[index]==',')) {
																indexmode=1;
																index++;
															  }
 while(index<strlen(line))
	{
	 index++;
	 if(!((line[index]==' ')||(line[index]=='\t')))
		break;
	}
 if (index<strlen(line))
	 return 1;
 return 0;
}

void extract(char *name,char *fname)
{
 for(int i=0;i<strlen(fname);i++)
 {
  if (fname[i]=='.') break;
  else
	  name[i]=fname[i];
 }
 name[i]='\0';
}